#pragma ident "$Id: PhaseBiasStore.hpp 2897 2011-09-14 20:08:46Z shjzhang $"

/**
 * @file PhaseBiasStore.hpp
 * Store a tabular list of satellite biaes, and compute values at any time
 * from this table. 
 */

//============================================================================
//
//  Shoujian Zhang, Wuhan University, 2012
//
//============================================================================

#include "PhaseBiasStore.hpp"
#include "MiscMath.hpp"
#include "DataStructures.hpp"

using namespace std;

#define debug 0

namespace gpstk
{

         /** Return value for the given satellite at the given time.
          *
          * @param sat      the SatID of the satellite of interest
          * @param time     the time (CommonTime) of interest
          * @return         object of type typeValueMap containing the data value(s).
          *
          * @throw InvalidRequest 
          *
          *  if data value cannot be computed because:
          *
          *  a) the time t does not lie within the time limits of the data table
          *  b) checkDataGap is true and there is a data gap
          *  c) checkInterval is true and the interval is larger than maxInterval
          *
          */
   typeValueMap PhaseBiasStore::getValue(const SatID& sat, const CommonTime& time)
      const noexcept(false)
   {
       try {

          bool isExact;
          typeValueMap biasData;
          DataTableIterator it1, it2, kt;  

          CivilTime cv(time);

             // Get the data range
          isExact = getTableInterval(sat, time, 1, it1, it2, true );

             // If the time is found in data table, then return directly
          if(isExact) 
          {
             biasData = it1->second;
             return biasData;
          }

             // Initial time
          CommonTime time0(it1->first);

             // Variables for linear interpolate
          vector<double> times;
          vector<typeValueMap> biasVec;

          kt = it1; 
          while(1) 
          {
                // Inesert time 
             times.push_back( kt->first - time0 );    // sec

             biasVec.push_back( kt->second);     

                // End of the data table
             if(kt == it2) break;

                // Increment
             ++kt;
          };

             // interpolate
          double dt(time-time0), slope;

          TypeIDSet typeSet = biasVec[0].getTypeID();

             // linear interpolation
          for(auto type: typeSet )
          {
              if(debug)
              cout << "PhaseBiasStore::"<< type  << endl;

              slope = (biasVec[1][type]-biasVec[0][type]) / (times[1]-times[0]);  

              biasData[type]= biasVec[0][type] + (dt)*slope;    // sec/sec

              if(debug)
              {
                 cout << "PhaseBiasStore:" 
                      << type 
                      << ":" << biasVec[0][type] 
                      << ":" << biasVec[1][type] << endl;
                 cout << "interpolated:" 
                      << biasData[type] 
                      << endl;
              }
          }

          return biasData;
       }
       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
   }

   satTypeValueMap PhaseBiasStore::getValue(const CommonTime& time)
      const noexcept(false)
   {
       satTypeValueMap satBiasData;
       try
       {
           SatIDSet satSet = getSatSet();
           for(auto sat: satSet)
           {
               typeValueMap biasData = getValue( sat, time);
               satBiasData[sat] = biasData;
           }
       }
       catch(InvalidRequest& e) 
       { GPSTK_RETHROW(e); }

       return satBiasData;
   }

   satValueMap PhaseBiasStore::getValue(const CommonTime& time, TypeID& type)
      const noexcept(false)
   {
       satValueMap satBiasData;
       try
       {
           SatIDSet satSet = getSatSet();
           for(auto sat: satSet)
           {
               typeValueMap biasData = getValue( sat, time);
               satBiasData[sat] = biasData(type);
           }
       }
       catch(InvalidRequest& e) 
       { GPSTK_RETHROW(e); }

       return satBiasData;
   }


      // Add a typeValueMap to the store.
   void PhaseBiasStore::addSatBiasData(const SatID& sat, 
                                       const CommonTime& time,
                                       const TypeID& type,
                                       const double& bias)
      noexcept(false)
   {
      try 
      {
          typeValueMap tvData;
          tvData = tables[sat][time];
          tvData[type] = bias;
          tables[sat][time] = tvData;
      }
      catch(InvalidRequest& ir) 
      { 
         GPSTK_RETHROW(ir); 
      }
   }

      /// Load a satellite upd file 
   void PhaseBiasStore::loadFile(const std::string& filename) 
       noexcept(false)
   {
      try 
      {
            // open the input stream
         PhaseBiasStream strm(filename.c_str());

         if(!strm.is_open()) 
         {
            Exception e("File " + filename + " could not be opened");
            GPSTK_THROW(e);
         }
         strm.exceptions(std::ios::failbit);

         if(debug)
         cout << "Opened file " << filename << endl;

            // declare header and data
         PhaseBiasHeader head;
         PhaseBiasData data;

            // read the RINEX clock header
         try 
         {
            strm >> head;
         }
         catch(Exception& e)
         {
            e.addText("Error reading header of file " + filename);
            GPSTK_RETHROW(e);
         }

         if(debug) head.dump();

            // read data
         try
         {
            while(strm >> data) 
            {
               if(debug)
               data.dump(cout);

               if(data.recType == std::string("AS")) 
               {
                   addSatBiasData(data.sat, data.time, data.type, data.bias);
               }
            }
              // close
            strm.close();
         }
         catch(EndOfFile& e)
         {
            strm.close();
            return;
         }
         catch(Exception& e) 
         {
            e.addText("Error reading data of file " + filename);
            GPSTK_RETHROW(e);

              // close
            strm.close();
         }
      }
      catch(Exception& e) 
      { 
          GPSTK_RETHROW(e); 
      }

   }  // end PhaseBiasStore::loadFile()


}  // End of namespace gpstk
